<?php

//Impotando os modelos e as entidades relacionadas
include_once ENTITIES.'item-prestacao-conta.php';
include_once ENTITIES.'prestacao-conta.php';
include_once MODELS.'item-prestacao-conta.php';
include_once MODELS.'prestacao-conta.php';

/**
 * Essa classe é quem controla o fluxo de informação do e para o modelo de tipos de
 * prestação de contas e de itens de prestação de contas.
 * @author Emanuel Oliveira <emanuel.oliveira23@gmail.com>
 */
class PrestacaoContaController {
    
    /**
     * @var PrestacaoContaModel instancia do modelo de prestações de conta
     * @access private
     */
    private $modelPrestacao;
    
    /**
     * @var PrestacaoContaModel instancia do modelo de itens de prestação de conta
     * @access private
     */
    private $modelItem;
    
    /**
     * Essa classe é quem controla o fluxo de informação do e para o modelo de tipos de
     * prestação de contas e de itens de prestação de contas.
     * @return PrestacaoContaController instância de um controlador de prestação de conta
     */
    public function __construct() {
        $this->modelPrestacao   = new PrestacaoContaModel();
        $this->modelItem        = new ItemPrestacaoContaModel();
    }
    
    /**
     * Este método adiciona ou atualiza uma prestação de conta no modelo.
     * @param PrestacaoConta $prestacao prestação de conta a ser inserida ou atualizada. 
     * Se for <b>null</b> os dados da requisição serão captados e atribuídos à <i>prestacao</i>
     */
    public function addPrestacao(PrestacaoConta $prestacao = null){
        
        //Verificando a necessidade de linkar os dados da requisição
        if($prestacao == null){
            $prestacao = new PrestacaoConta();
            $this->linkPrestacao($prestacao);
        }
        
        $config = Config::getInstance();
        
        //Validando os dados informados
        if(empty($prestacao->cobrador) || empty($prestacao->dtFinal) || empty($prestacao->dtInicial)){
            $config->failInFunction('Os Campos Cobrador, Data Incial e Data Final são obrigatórios');
            $config->redirect('index.php?op=cad-prest-conta');
        }
        
        //Checando as datas
        if(strtotime($prestacao->dtInicial) > strtotime($prestacao->dtFinal)){
            $config->failInFunction('Data final deve ser maior do que a inicial');
            $config->redirect('index.php?op=cad-prest-conta');
        }
        
        if(empty($prestacao->itens)){
            $config->failInFunction('Itens inválidos');
            $config->redirect('index.php?op=cad-prest-conta');
        }
        
        $hasId  = $config->filter('for-update-id');
        
        //Verificando se é necessário o fechamento de uma prestação de conta
        if($prestacao->status){
            //Verificando a existência dos itens associados 
            //(uma prestação de conta só pode ser fechada quando exisytem itens associados)
            if(empty($prestacao->itens)){
                $config->failInFunction('Sem itens para fechar a prestação');
                $config->redirect('index.php?op=cad-prest-conta');
            }
            if(!empty($hasId)){
                //Validação dos valores da prestação de conta
                include_once CONTROLLERS.'parcela.php';
                $parcela_controller = new ParcelaController();
                $pagamentos         = $parcela_controller->getPagamentosByPrestacao($hasId);
                //Total contabilizado nos pagamentos
                $totalPag           = 0;
                //Total contabilizado nos itens
                $totalItens         = 0;
                foreach ($pagamentos as $p) { $totalPag += $p->valor; }
                foreach ($prestacao->itens as $i) {$totalItens += $i->valor;}
                //Validação real
                if($totalItens != $totalPag){
                    $config->failInFunction('Os totais de pagamentos e de itens deve ser iguais!');
                    $config->redirect('index.php?op=cad-prest-conta');
                }
            }
        }
        
        //Resultado da operação
        $res    = false;
        
        //Iniciando transação
        $this->modelPrestacao->begin();
        
        //Vereficando tipo de operação (inserção ou atualização)
        if(empty($hasId)){
            //Operação de inserção
            $prestacao->seq = $this->modelPrestacao->maxSeq($prestacao->cobrador) + 1;
            $res = $this->modelPrestacao->insert($prestacao);
            if($res && !empty($prestacao->itens)){
                //Adicionando os itens da prestação de contas
                foreach ($prestacao->itens as &$item){ $item->prestacao = $prestacao->id; }
                $res = $this->modelItem->inserts($prestacao->itens);
            }
        } else {
            //Operação de atualização
            $prestacao->id = $hasId;
            
//            Validação Cancelada (Solicitação Usuário)
//            $somaItens      = $this->getValorOfPrestacao($prestacao->id);
//            $somaRequisicao = 0;
//            foreach ($prestacao->itens as $i) {$somaRequisicao += $i->valor;}
//            
//            if($somaRequisicao < $somaItens){
//                if(!isset($parcela_controller)){
//                    //Validação dos valores da prestação de conta
//                    include_once CONTROLLERS.'parcela.php';
//                    $parcela_controller = new ParcelaController();
//                }
//                $somaPagamentos = $parcela_controller->getPagamentosByPrestacao($hasId);
//                if($somaRequisicao < $somaPagamentos){
//                    $config->failInFunction('Valor de itens insuficiente.');
//                    $config->redirect('index.php?op=cad-prest-conta');
//                }
//            }
            
            //Atualizando e removendo os itens
            $res =  $this->modelPrestacao->update($prestacao) && 
                    $this->modelItem->deleteFromPrestacao($prestacao->id);
            //Caso haja novos itens...
            if(!empty($prestacao->itens)){ 
                //... Eles serão inseridos
                foreach ($prestacao->itens as &$item){ $item->prestacao = $prestacao->id; }
                $res = $res && $this->modelItem->inserts($prestacao->itens);
            }
        }
        if($res){
            //Sucesso na operação
            $this->modelPrestacao->commit();
            $config->successInFunction();
        }  else {
            //Falha na operação
            $this->modelPrestacao->rollBack();
            $config->failInFunction($this->modelPrestacao->handleError());
        }
        $config->redirect('index.php?op=cad-prest-conta');
    }
    
    /**
     * Esse método linka os dados da requisção que diz respeito à prestação de conta 
     * com um objeto passado como referência
     * @param PrestacaoConta $prestacao uma referência de uma prestação de contas que 
     * vai ser preenchida com os dados da requisição
     * @access private
     */
    public function linkPrestacao(PrestacaoConta &$prestacao){
        $config = Config::getInstance();
        $prestacao->cobrador    = $config->filter('cobrador');
        $status                 = $config->filter('status');
        $prestacao->status      = !empty( $status );
        $prestacao->dtInicial   = $config->filter('data-inicial');
        $timeInicial            = strtotime($prestacao->dtInicial);
        $prestacao->dtFinal     = $config->filter('data-final');
        $timeFinal              = strtotime($prestacao->dtFinal);
        $prestacao->itens       = array();
        $itens = explode(';', $config->filter('itens-prestacao'));
        foreach ($itens as $itemStr) {
            if(empty($itemStr)) continue;
            $item   = explode(':', $itemStr);
            $valor  = (float) str_replace(',', '.', $item[0]);
            $tipo   = (int) $item[1];
            $data   = $item[2]; 
            $timeData   = strtotime($data);
            if(empty($data) || $timeData < $timeInicial || $timeData > $timeFinal){
                $prestacao->itens = null;
                break;
            }
            if(empty($valor) || empty($tipo)) continue;
            $prestacao->itens[] = new ItemPrestacaoConta(0, $valor, $tipo, $data);
        }
    }
    
    /**
     * Remove uma prestação de conta da base de dados de acordo com o identificador passado na requisição
     */
    public function removePrestacao(){
        $config = Config::getInstance();
        if(!$config->checkGerentConfirm()){
            $config->failInFunction('Essa função necessita da autorização do gerente');
            $config->redirect('index.php?op=cad-prest-conta');
        }
        $prest  = $config->filter('prest');
        if(!empty($prest)){
            $condition = PrestacaoContaModel::ID. ' = ' . $prest;
            if($this->modelPrestacao->simpleUpdate(PrestacaoContaModel::CANCELADA, '1', $condition)){
                $config->successInFunction();
            } else {
                $config->failInFunction($this->modelPrestacao->handleError());
            }
        } else {
            $config->failInFunction('Prestação não informada');
        }
        $config->redirect('index.php?op=cad-prest-conta');
    }
    
    /**
     * Obtém todas as prestações de conta do modelo.
     * @param bool $foreign_values se for true, os valores textuais das chaves estrangeiras 
     * serão atribuídos no lugar dos indexes inteiros.
     * @param int $byLoja indica se deve ser filtrador por loaj, se false é desconsiderado
     * @param boolean $only_abertas indica se somente as prestações de conta em aberto devem ser retornadas
     * @return array lista de prestações de conta do tipo PrestacaoConta.
     */
    public function getAllPrestacaoConta($foreign_values = false, $byLoja = false, $only_abertas = false){
        $condition = PrestacaoContaModel::CANCELADA.' = 0 ';
        if($byLoja){
            $condition .= ' AND '. FuncionarioModel::LOJA.' = '.$byLoja;
        }
        if($only_abertas) 
            $condition .= ' AND '.PrestacaoContaModel::TABLE.'.'.PrestacaoContaModel::STATUS.' = '.PrestacaoContaModel::STATUS_ABERTA;
        
        $condition .= ' ORDER BY '.PrestacaoContaModel::COBRADOR.', '.PrestacaoContaModel::SEQ;
        if($foreign_values) return $this->modelPrestacao->superSelect($condition);
        return $this->modelPrestacao->select($condition);
    }
    
    /**
     * Obtém uma prestação de conta em específica do modelo.
     * @param int $id_prest valor do identificador de uma prestação de conta
     * @return PrestacaoConta uma prestação de conta correspondente ao identificador 
     * ou uma vazia em caso de inexistência
     */
    public function getPrestacaoConta($id_prest){
        $condition  = PrestacaoContaModel::TABLE.'.'.PrestacaoContaModel::ID.' = '.$id_prest;
        $res        = $this->modelPrestacao->select('*', $condition);
        if(empty($res)) return new PrestacaoConta();
        return $res[0];
    }
    
    /**
     * Obtém os itens associados a uma prestação de conta
     * @param int $prest identificador da prestação de conta
     * @param bool $foreign_values se for true, os valores textuais das chaves estrangeiras 
     * serão atribuídos no lugar dos indexes inteiros.
     * @return array lista de prestações de conta do tipo PrestacaoConta.
     */
    public function getItensByPrestacao($prest, $foreign_values = false){
        $condition = ItemPrestacaoContaModel::PRESTACAO.' = '.$prest;
        if($foreign_values) return $this->modelItem->superSelect($condition);
        return $this->modelItem->select('*', $condition);
    }
    
    /**
     * Obtém as prestações de conta associadas a um cobrador
     * @param int $cobrador identificador do cobrador
     * @param boolean $only_abertas indica se somente as prestações de conta em aberto devem ser retornadas
     * @return array lista de prestações de conta do tipo PrestacaoConta.
     */
    public function getPrestacoesByCobrador($cobrador, $only_abertas = false){
        $condition  = PrestacaoContaModel::COBRADOR.' = '.$cobrador;
        $condition .= ' AND '.PrestacaoContaModel::CANCELADA.' = 0 '; 
        if($only_abertas) 
            $condition .= ' AND '.PrestacaoContaModel::TABLE.'.'.PrestacaoContaModel::STATUS.' = '.PrestacaoContaModel::STATUS_ABERTA;
        return $this->modelPrestacao->select('*', $condition);
    }
 
    /**
     * Obtém o valor de uma prestação de conta, somando os itens associados a ela.
     * @param int $prestacao identificador da prestação de conta
     * @return float soma dos valores dos itens projetados 
     */
    public function getValorOfPrestacao($prestacao){
        $condition = ItemPrestacaoContaModel::PRESTACAO.' = '.$prestacao;
        return $this->modelItem->getSomaValores($condition);
    }
    
    /**
     * Altera o status de uma prestação de conta para '0', onde significa a abertura dela.
     * Os identificadores são obtidos através da requisição.
     */
    public function reabrirPrestacao(){
        
        $config = Config::getInstance();
        if(!$config->checkGerentConfirm()){
            $config->failInFunction('Essa função necessita da autorização do gerente');
            $config->redirect('index.php?op=cad-prest-conta');
        }
        $prest      = $config->filter('prest');
        $condition  = PrestacaoContaModel::ID.' = '.$prest;
        if($this->modelPrestacao->simpleUpdate(PrestacaoContaModel::STATUS, '0', $condition)){
            $config->successInFunction();
        } else {
            $config->failInFunction($this->modelPrestacao->handleError());
        }
        $config->redirect('index.php?op=cad-prest-conta');
    }
    
}
?>
